/*
 *                          许可协议    
 *    
 *  COPYRIGHT ©2008 The Siox software project Lab.
 * 
 * 版权声明:                               
 *  1.任何"派生软件"都必须包含此许可协议,"派生软件的开发者"必   
 *  须完全保留被继承"软件"的许可协议和"版权信息".          
 *  2."派生软件开发者"在别人使用他所"派生"的"软件"时,自动成为"开
 *  发者".                               
 *  3."使用者"所生成的二进制代码文件须包含"原始开发者"的版权信息. 
 *  4.如该软件并未得到"开发者"的开源，那么"使用者"对本程序仅有使  
 *  用权，并不得进行反向工程.                      
 *  5.本协议保留"原始开发者"的版权信息在许可协议文本之内.
 *  6.本文档作为源代码/程序的基本协议,如有变化作为本协议扩充 
 *                                     
 * 免责声明:                               
 *  1.使用本"软件"都意味"使用者"是自愿使用本"软件",接受此许可协 
 *  议的所有条款.如果"使用者"不同意此许可协议就不能使用本"软件".  
 *  2.本"软件"虽然在开发者的开发环境下经过严格测试,但不能排除    
 *  "软件"中仍然存在错误.由于使用本"软件"所造成的任何损失,"开发  
 *  者"不负任何责任!                          
 *  3.本"软件"在被"使用"过程中,由"使用者"发现的任何错误必须再反 
 *  馈给"开发者"并经过"开发者"同意后,才可对其他人公开.任何未经过  
 *  "开发者"同意而被使用者擅自公开的本"软件"的错误,"开发者"有权  
 *  要求其对"开发者"名誉损失负法律责任. 
 * 
 *  个人主页:http://hi.baidu.com/lindily
 *  电子信箱:lindily@126.com               
 */
package cn.siox.util;

/**
 * @类名 IDCard
 * @作者 Lindily
 * @日期 2008-11-20 16:56:13
 */

/**
 java实现的18位身份证格式验证算法
　　公民身份号码是特征组合码,由十七位数字本体码和一位数字校验码组成.排列顺序从左至右依次为:六位数字地址码,
    八位数字出生日期码,三位数字顺序码和一位数字校验码。
　
1、地址码
表示编码对象常住户口所在县（市、旗、区）的行政区划代码，按 GB/T 2260 的规定执行。

2、出生日期码
表示编码对象出生的年、月、日，按 GB/T 7408 的规定执行。年、月、日代码之间不用分隔符。
例：某人出生日期为 1966年10月26日，其出生日期码为 19661026。

3、顺序码
表示在同一地址码所标识的区域范围内，对同年、同月、同日出生的人编定的顺序号，顺序码的奇数分配给男性，偶数千分配给女性。

4 、校验码
校验码采用ISO 7064：1983，MOD 11-2 校验码系统。

　 （1）十七位数字本体码加权求和公式
　　　 S = Sum(Ai * Wi), i = 0, ... , 16 ，先对前17位数字的权求和
　　　 Ai:表示第i位置上的身份证号码数字值
　　　 Wi:表示第i位置上的加权因子
　　　 Wi: 7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2

　 （2）计算模
　　　 Y = mod(S, 11)

　 （3）通过模得到对应的校验码
　　　　Y:　　 0 1 2 3 4 5 6 7 8 9 10
　　　　校验码: 1 0 X 9 8 7 6 5 4 3 2
 */
public class IDCard {
// wi =2(n-1)(mod 11)
    final int[] wi = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2, 1};

// verify digit
    final int[] vi = {1, 0, 'X', 9, 8, 7, 6, 5, 4, 3, 2};
    private int[] ai = new int[18];

    public IDCard() {
    }

//verify
    public boolean Verify(String idcard) {
        if (idcard.length() == 15) {
            idcard = uptoeighteen(idcard);
        }
        if (idcard.length() != 18) {
            return false;
        }
        String verify = idcard.substring(17, 18);
        if (verify.equals(getVerify(idcard))) {
            return true;
        }
        return false;
    }

//get verify
    public String getVerify(String eightcardid) {
        int remaining = 0;

        if (eightcardid.length() == 18) {
            eightcardid = eightcardid.substring(0, 17);
        }

        if (eightcardid.length() == 17) {
            int sum = 0;
            for (int i = 0; i < 17; i++) {
                String k = eightcardid.substring(i, i + 1);
                ai[i] = Integer.parseInt(k);
            }

            for (int i = 0; i < 17; i++) {
                sum = sum + wi[i] * ai[i];
            }
            remaining = sum % 11;
        }

        return remaining == 2 ? "X" : String.valueOf(vi[remaining]);
    }

//15 update to 18
    public String uptoeighteen(String fifteencardid) {
        String eightcardid = fifteencardid.substring(0, 6);
        eightcardid = eightcardid + "19";
        eightcardid = eightcardid + fifteencardid.substring(6, 15);
        eightcardid = eightcardid + getVerify(eightcardid);
        return eightcardid;
    }

    public static void main(String[] args) {
        String id="620402198311141334";
        IDCard card=new IDCard();
        System.out.println(card.Verify(id));
        System.out.println(card.getVerify(id));
        System.out.println(card.uptoeighteen("620402591227132"));

    }
}